package contegor.scire.core;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Interface for concept containers.
 *
 * @version $Revision: 1.13 $
 * @author  Héctor Cirbián Pousa ($Author: hector $)
 */
@Version("$Id: Container.java,v 1.13 2010-02-01 10:55:38 hector Exp $")
public interface Container extends Identifiable, Connectable {

  /** Shows if an {@link Identifiable} exists inside <code>this</code>. */
  public boolean contains(IdFragment id);

  /** Shows if an {@link Connectable} belongs to <code>this</code>. */
  public boolean contains(Connectable obj);

  /** Returns a {@link Concept} contained in <code>this</code>.*/
  public Concept getConcept(IdFragment id);

  /** Returns all {@link Concept}s contained in <code>this</code>. No containers will be returned. */
  public List<Concept> getConcepts();

  /** Returns the {@link IdFragment} or local identifiers from all elements in <code>this</code>. */
  public Set<IdFragment> getLocalIds();

  /** Returns the number of elements inside <code>this</code>. */
  public int size();

  /**
   * Returns the {@link Set} of {@link Link} instances inside <code>this</code> who's target are the {@link Identifiable} identified by
   * the {@link Id} parameter. Such target {@link Identifiable} can be in <code>this</code> or any other {@link Container}.
   */
  public Set<Link> getLinksReferring(Id target);

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Gives the number of {@link Link}s inside <code>this</code> who's target have the given root {@link Id}.
   * 
   * @param root {@link Id} to select targets.
   * @return {@link Map} with the full target {@link Id}s and the number of {@link Link}s aiming them. 
   */  
  public Map<Id, Integer> getNumberOfLinksTo(Id root);

  /**
   * Returns a {@link Set} with the {@link Id}s of {@link Container} that contain {@link Link} instances who's target
   * is the {@link Identifiable} belonging to <code>this</code> identified by the {@link IdFragment} parameter.
   * 
   * @param target {@link IdFragment} of the {@link Identifiable} inside <code>this</code> connected with the items searched.
   * @return {@link Set} with the {@link Id}s of the {@link Container}s searched.
   */
  public Set<Id> getContainersReferring(IdFragment target);

  /**
   * Delivers a {@link Task}. If the target of that {@link Task} belongs to <code>this</code>, then
   * it is processed with {@link #process(Task)}. if not, it is transfered to the apropiate {@link Container}.
   * 
   * @param task {@link Task} to be processed.
   * @param rp {@link ReturnPoint} where the routing and processing results will be forwarded.
   */
  public <R> void route(Task<R> task, ReturnPoint<? super R> rp);

  /**
   * <b>Internal use only method. Use instead {@link #route(Task)}.</b>
   * This method routes the {@link Task} to the target {@link Container}. If <code>this</code> is such
   * {@link Container} it will be processed with {@link #process(Task)}.
   * It is a recursive method. It uses the number of common ancestors between <code>this</code> and the
   * the target {@link Container} in order to accomplish an efective routing.
   * 
   * @param task {@link Task} to be routed.
   * @param commonAncestors <code>int</code> with the number of common ancestors between <code>this</code> and the
   * the target {@link Container}.
   * @param rp {@link ReturnPoint} where the routing and processing results will be forwarded.
   */
  public <R> void route(Task<R> task, int commonAncestors, ReturnPoint<? super R> rp);

  /**
   * <b>Internal use only method. Use instead {@link #route(Task)}.</b>
   * Does the {@link Task} processing. This method implementations must do task processing through the next steps:
   * 0) Call {@link ReturnPoint#start(TaskId)} with {@link Task}'s {@link TaskId}. 
   * 1) Process {@link Task}'s dependencies if it has (is a {@link WithDependencies}).
   * 2) Wait all dependencies are finished.
   * 3) Process the {@link Task}.
   * 4) Wait the {@link Task} processing is finished.
   * 5) Process {@link Task}'s consequences if it has (is a {@link WithConsequences}).
   * 6) Wait all consequences are finished.
   * 7) Process {@link Task}'s subsequent tasks if it has (is a {@link WithSubsequentTasks}).
   * 8) Wait all subsequent tasks are finished.
   * 9) Call {@link ReturnPoint#finish(TaskId)} with {@link Task}'s {@link TaskId}. 
   * 
   * @param task {@link Task} to be processed.
   * @param rp {@link ReturnPoint} where the routing and processing results will be forwarded.
   */
  public <R> void process(Task<R> task, ReturnPoint<? super R> rp);
 
  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Notifies <code>this</code> that one of its elements is pointed out by {@link Link}s from other {@link Container}.
   * This method will be used only when this {@link Container} is connected with other one.
   * No other 'tracking events' from that other {@link Container} should be active. So at the same time
   * there can't be executions of this method and of {@link Container#trackExternalLinks(IdFragment, Id)}
   * and {@link Container#untrackExternalLinks(IdFragment, Id)} for the same 'externalContainer'.
   * 
   * @param targetConcept {@link IdFragment}. Must belong to <code>this</code>.
   * @param externalContainer {@link Id}, {@link Container} to which belongs the {@link Link}s that points out
   *        the element inside <code>this</code>.
   * @param references int with the number of {@link Link}s pointing the target inside <code>this</code>.
   * @throws MissingElementException
   */
  public void setTrackedExternalLinks(IdFragment targetConcept, Id externalContainer, int references) throws MissingElementException;
  
  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Notifies <code>this</code> that one of its elements is pointed out by {@link Link}s from other {@link Container}.
   * 
   * @param targetConcept {@link IdFragment}. Must belong to <code>this</code>.
   * @param externalContainer {@link Id}, {@link Container} to which belongs the {@link Link}
   *        that points out the element inside <code>this</code>.
   * @param number the amount of {@link Link}s.
   * @throws MissingElementException if <code>targetConcept</code> is missing.
   */
  public void trackExternalLinks(IdFragment targetConcept, Id externalContainer, int number) throws MissingElementException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Notifies <code>this</code> that one of its elements is not more pointed out by defined number of {@link Link}s
   * from an other {@link Container}.
   * 
   * @param targetConcept {@link IdFragment}. Must belong to <code>this</code>.
   * @param externalContainer {@link Id}, {@link Container} to which belongs the {@link Link}
   *        that was pointing the element inside <code>this</code>.
   * @param number the amount of {@link Link}s.
   * @throws MissingElementException if <code>targetConcept</code> is missing.
   */
  public void untrackExternalLinks(IdFragment targetConcept, Id externalContainer, int number) throws MissingElementException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Notifies <code>this</code> to stop tracking external links from other {@link Container}s that have
   * the given root {@link Id}. 
   * 
   * @param root {@link Id} of the {@link Container} who's {@link Link}s should be no more tracked.
   */
  public void untrackExternalLinks(Id root);

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Returns pending transactions in this {@link Container}.
   * 
   * @return {@link Set} with pending transactions {@link TransactionId}s.
   */
  public Set<TransactionId> getPendingTransactions();
}
